#include "lib_MonitorProcess.h"
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <time.h>
#include <algorithm>
#include <proc/readproc.h>
using namespace std;

MonitorProcess::MonitorProcess(MonitorCpu* monitorCpu_impl, MonitorMem* monitorMem_impl)
    :monitorCpu_impl_(monitorCpu_impl), monitorMem_impl_(monitorMem_impl)
{
    cpu_number_ = monitorCpu_impl_->get_cpu_num();
    allProcess_[0].read_count = 0;
    allProcess_[1].read_count = 0;
    allProcess_[0].process_list.resize(MAX_PROCESS_NUM);
    allProcess_[1].process_list.resize(MAX_PROCESS_NUM);
    allProcess_[1].cpu_time = 0;
    update_allProcCpuTime(false);
}

MonitorProcess::~MonitorProcess()
{

}

float MonitorProcess::cal_process_cpuUsage(processInfo_t& prev, processInfo_t& curr, unsigned long long dt_cpu_time, allProcessInfo_t* prev_all_proc)
{
    processInfo_t* find_proc = &prev;
    if (prev.pid != curr.pid)
    {
        find_proc = find_processInfo_by_pid(curr.pid, prev_all_proc);
        if (find_proc == NULL)
        {
            return 0.0;
        }
    }
    cal_proc_io_speed(&find_proc->io, &curr.io);
    return ((curr.utime + curr.stime) - (find_proc->utime + find_proc->stime)) * 100.0 * cpu_number_ / dt_cpu_time;
}

allProcessInfo_t* MonitorProcess::update_allProcCpuTime(bool opt_calCpuUsage)
{
    PROCTAB* proc_tab = openproc(PROC_FILLMEM | PROC_FILLSTAT);
    if (proc_tab == NULL)
    {
        perror("openproc failed");
        return NULL;
    }

    allProcessInfo_t* prev, * curr;
    if (allProcess_[0].read_count == allProcess_[1].read_count)
    {
        curr = &allProcess_[0];
        prev = &allProcess_[1];
    }
    else if (allProcess_[0].read_count > allProcess_[1].read_count)
    {
        curr = &allProcess_[1];
        prev = &allProcess_[0];
    }
    else
    {
        fprintf(stderr, "逻辑错误, 请检查代码\n");
        exit(EXIT_FAILURE);
    }
    curr->cpu_time = monitorCpu_impl_->get_cpu_time();
    unsigned long long dt_cpu_time = curr->cpu_time - prev->cpu_time;
    unsigned long long total_mem = monitorMem_impl_->get_total_mem();
    curr->process_list.clear();

    proc_t proc_info, * pt_ret;
    processInfo_t tmp_proc;
    size_t i = 0; // 进程数量
    while (i < MAX_PROCESS_NUM)
    {
        memset(&proc_info, 0, sizeof(proc_info));
        memset(&tmp_proc, 0, sizeof(tmp_proc));
        pt_ret = readproc(proc_tab, &proc_info);
        if (pt_ret == NULL) {
            break;
        }

        // 应用进程
        if (proc_info.cmdline != NULL) {
            tmp_proc.pid = proc_info.tid;
            strncpy(tmp_proc.name, proc_info.cmd, BUF_64 - 1);
            strncpy(tmp_proc.cmdline, *proc_info.cmdline, BUF_256 - 2);
            tmp_proc.state = proc_info.state;
            tmp_proc.utime = proc_info.utime;
            tmp_proc.stime = proc_info.stime;
            tmp_proc.starttime = proc_info.start_time;
            tmp_proc.nice = proc_info.nice;
            tmp_proc.rss = proc_info.rss;
        }
        // 内核进程
        else {
            tmp_proc.pid = proc_info.tid;
            size_t cmd_len = strlen(proc_info.cmd);
            if (cmd_len > BUF_64 - 4) {
                cmd_len = BUF_64 - 4;
            }
            snprintf(tmp_proc.name, BUF_64, "%.*s", (int)cmd_len, proc_info.cmd);
            tmp_proc.state = proc_info.state;
            tmp_proc.utime = proc_info.utime;
            tmp_proc.stime = proc_info.stime;
            tmp_proc.starttime = proc_info.start_time;
            tmp_proc.nice = proc_info.nice;
            tmp_proc.rss = proc_info.rss;
        }

        // 统计进程io
        get_proc_io(tmp_proc.pid, &tmp_proc.io);

        // 计算cpu使用率
        if (opt_calCpuUsage)
        {
            if (dt_cpu_time)
                tmp_proc.cpu_usage = cal_process_cpuUsage(prev->process_list[i], tmp_proc, dt_cpu_time, prev);
            else
            {
                tmp_proc.cpu_usage = 0;
                tmp_proc.mem_usage = 0;
            }
        }
        // 计算内存使用率
        if (total_mem)
            tmp_proc.mem_usage = tmp_proc.rss * 4096 * 100.0 / total_mem;

        curr->process_list.push_back(tmp_proc);
        // freeproc(&proc_info);
        i++;
    }
    closeproc(proc_tab);
    curr->proc_number = i;
    curr->read_count++;

    return curr;
}

void MonitorProcess::show_allProcessInfo(allProcessInfo_t* all_proc, uint32_t num)
{
    if(num == 0)
        num = all_proc->proc_number;
    else
        num = min(num, all_proc->proc_number);

    processInfo_t* proc;
    printf("%-8s %-16s %-8s %-8s %-8s\n", "pid", "cmd", "cpu%", "r_speed B/s", "w_speed B/s");
    for (size_t i = 0; i < num; i++)
    {
        proc = &all_proc->process_list[i];
        printf("%-8d %-16s %-8.2f %-8.2f %-8.2f\n", proc->pid, proc->name, proc->cpu_usage, proc->io.read_speed, proc->io.write_speed);
    }
}

processInfo_t* MonitorProcess::find_processInfo_by_pid(pid_t pid, allProcessInfo_t* all_proc)
{
    for (size_t i = 0; i < all_proc->proc_number; i++)
    {
        if (all_proc->process_list[i].pid == pid)
            return &all_proc->process_list[i];
    }
    return NULL;
}


bool compare_by_cpuUsage(const processInfo_t& a, const processInfo_t& b)
{
    // 升序
    // return a.cpu_usage < b.cpu_usage;

    // 降序
    return a.cpu_usage > b.cpu_usage;
};

allProcessInfo_t* MonitorProcess::sort_proc_by_cpuUsage(allProcessInfo_t* all_proc)
{
    sort_proc_ = *all_proc;
    sort(sort_proc_.process_list.begin(), sort_proc_.process_list.end(), compare_by_cpuUsage); // 升序排序
    return &sort_proc_;
}

ProcessIO_t* MonitorProcess::get_proc_io(pid_t pid, ProcessIO_t* io)
{
    clock_gettime(CLOCK_MONOTONIC, &io->ts);

    char path[64] = { 0 };
    snprintf(path, sizeof(path), "/proc/%d/io", pid);
    FILE* fp = fopen(path, "r");
    if (fp == NULL)
    {
        io->read_bytes = 0;
        io->write_bytes = 0;
        return NULL;
    }

    char buf[256];
    while (fgets(buf, sizeof(buf), fp) != NULL)
    {
        if (strncmp(buf, "read_bytes:", 11) == 0)
            io->read_bytes = strtoull(buf + 11, NULL, 10);
        else if (strncmp(buf, "write_bytes:", 12) == 0)
        {
            io->write_bytes = strtoull(buf + 12, NULL, 10);
            break;
        }
    }
    fclose(fp);

    return io;
}

float MonitorProcess::cal_proc_io_speed(ProcessIO_t* prev, ProcessIO_t* curr)
{
    if (prev == NULL || curr == NULL)
        return 0;

    float dt_time = (curr->ts.tv_sec - prev->ts.tv_sec) + (curr->ts.tv_nsec - prev->ts.tv_nsec) * 1e-9;
    if (dt_time == 0)
        return 0;

    if (curr->read_bytes > prev->read_bytes)
        curr->read_speed = (curr->read_bytes - prev->read_bytes) / dt_time;
    else
        curr->read_speed = curr->read_bytes / dt_time;

    if (curr->write_bytes > prev->write_bytes)
        curr->write_speed = (curr->write_bytes - prev->write_bytes) / dt_time;
    else
        curr->write_speed = curr->write_bytes / dt_time;

    return curr->write_speed;
}


allProcessInfo_t MonitorProcess::filter_proc_by_name(allProcessInfo_t* all_proc, vector<string>& filter_name)
{
    allProcessInfo_t result;
    result.proc_number = 0;
    for (string& key : filter_name)
    {
        for (processInfo_t& proc : all_proc->process_list)
        {
            if (strcmp(proc.name, key.c_str()) == 0)
            {
                result.process_list.push_back(proc);
                result.proc_number++;
            }
        }
    }
    return result;
}